package com.foreveross.crawl.adapter.sub.impl20140402.v3;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.foreveross.crawl.adapter.AbstractAdapter;
import com.foreveross.crawl.adapter.PlaneInfoEntityBuilder;
import com.foreveross.crawl.common.util.RegHtmlUtil;
import com.foreveross.crawl.domain.airfreight.AbstractPlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.CabinEntity;
import com.foreveross.crawl.domain.airfreight.TransitEntity;
import com.foreveross.crawl.domain.airfreight.doub.CabinRelationEntity;
import com.foreveross.crawl.domain.airfreight.doub.DoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnCabinEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnDoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnTransitEntity;
import com.foreveross.crawl.exception.FlightInfoNotFoundException;
import com.foreveross.crawl.util.DateUtil;
import com.foreveross.taskservice.common.bean.TaskModel;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * 阿联酋航空公司手机网站适配器 。
 * 
 * @author luomingliang@foreveross.com
 * @date 2014.07.04
 * @version 1.0.0
 */
public class EmiratesAdapter extends AbstractAdapter {

	private final static int cabin_retry_count = 5; // 仓位信息抓取失败重试次数
	private final static int detail_retry_count = 3; // 详细数据加载失败重试次数

	private final static boolean load_more_cabin_e = true; // 是否加载所有舱位，false 只加载经济舱。

	// 详细信息地址
//	private final static String flightUrl = "http://mobile1.emirates.com/english/CAB/IBE/searchResults.xhtml";
//	private final static String detail_url = "http://mobile1.emirates.com/english/CAB/IBE/undefined/MAB/searchByPrice/loadSearchByPriceDetails.xhtml?index=%s";

	private final static String flightUrl = "https://mobile.emirates.com/cn/chinese/CAB/IBE/searchResults.xhtml";
	private final static String detail_url = "https://mobile.emirates.com/cn/chinese/CAB/IBE/loadFlightItinDetails.xhtml?index=%s";//去程详情
	private final static String re_detail_url ="https://mobile.emirates.com/cn/chinese/CAB/IBE/getBrandedSearchResultsInbound.xhtml";//回程详情
	private final static String all_detail_url="https://mobile.emirates.com/cn/chinese/CAB/IBE/reviewItinerary.xhtml";//最后一层完整信息url
	
	public EmiratesAdapter(TaskModel taskQueue) {
		super(taskQueue);
	}

	@Override
	public String getUrl() throws Exception {
		return null;
	}

	@Override
	public Object fetch(String url) throws Exception {
		switch (super.getRouteType()) {
		case INTERNATIONAL_ONEWAY:
			return (List<Object>) fetchInterOneWay();
		case INTERNATIONAL_ROUND:
			return (List<Object>) fetchInterRound();
		default:
			return null;
		}
	}

	@Override
	public List<Object> paraseToVo(Object obj) throws Exception {
		List<Object> objs = (List<Object>) obj;
		// printJson(objs); //输出Json数据
		return objs;
	}

	@Override
	public boolean validateFetch(Object obj) throws Exception {
		return true;
	}

	/**
	 * ******** FETCHDATA *************************************************************************************
	 * */

	/** 获取单个仓位的所有航班数量。 */
		HttpRequestBase request = null;
	private String fetchPageDatas(Integer cabinType) throws Exception {
		String page = null;
		Integer totalSize = 0;
		try {
			// 重试进行抓取
			for (int i = 0; i < cabin_retry_count; i++) {
				try {
					request = new HttpPost(getFetchUrl(cabinType));
					page = super.excuteRequest(getHttpClient(), request, true);
					super.setLenghtCount(page.length());
					totalSize = Integer.parseInt(RegHtmlUtil.regStr(page, regexMap.get("reg_totalcount"), 1));
					logger.info(String.format("舱位[%s]加载成功，数量[%s]", CABIN.get(cabinType), totalSize));
					break;
				} catch (Exception e) {
					logger.error(String.format("舱位[%s]信息加载失败，进行第[%s]次重试", CABIN.get(cabinType), i + 1));
					rollbackProxyIp(false);
					switchProxyipByHttClient();
				}
			}
		} catch (Exception e) {
			throw e;
		} finally {
			request = null;
		}
		rollbackProxyIp(true);
		return page; // 重试之后还是不能成功
	}

	/**详细信息URL*/
	private String getDeataUrl(int f){
		StringBuffer url=new StringBuffer();
		url.append(detail_url);

		// 单程
		if (taskQueue.getIsReturn() == 0) {
			url.append("&searchOrigin=ON");
		}else{
			//往返
			url.append("&searchOrigin=RE");
		}
		if(f==2){
			url.append("&inBound=true");
			url.append("&routeTypeOrSegNumber=IB");
			url.append("&stepCount=2");//返程详细信息
		}else{
			url.append("&inBound=false");
			url.append("&routeTypeOrSegNumber=OB");
			url.append("&stepCount=1");//去程详细信息
		}
		
		url.append("&pageIdentifier=searchByPriceResults&multiLowestPrice=false&isAjax=true");
		return url.toString();
	}
	
	/***/
	public String getRDetailURL(String urls,String inBrandCode,int inOption,String outBrandCode,int outOption){
		StringBuffer url=new StringBuffer(urls);
		url.append("?fromPage=searchResultPage");
		url.append("&inBrandCode="+(inBrandCode==null?"":inBrandCode));
		url.append("&inOptionDivId="+(inBrandCode==null?"":inOption));
		url.append("&inOptionId="+inOption);
		url.append("&inboundPageCount="+(inOption/5>0?inOption/5:inOption/5+1));
		
		url.append("&outBrandCode="+outBrandCode);
		url.append("&outOptionDivId="+outOption);
		url.append("&outOptionId="+outOption);
		url.append("&outboundPageCount="+(outOption/5>0?outOption/5:outOption/5+1));
		
		return url.toString();
	}
	
	/** 获取URL */
	private String getFetchUrl(Integer cabinType) {
		StringBuffer url = new StringBuffer();
		String[] goDps = null;
		String[] reDps = null;
		try {
			goDps = taskQueue.getFlightDate().split("-");

			url.append(flightUrl);
			
			//new
			url.append("?bookingType=Revenue&totalAdults=1&totalChildren=0&totalInfants=0&destInterlineFlag=false&flexiDate=false&originInterlineFlag=false");
			url.append("&fromCity=" + taskQueue.getFromCity());
			url.append("&toCity=" + taskQueue.getToCity());
			url.append("&departDay=" + goDps[2]);
			url.append("&departMonth=" + goDps[1]);
			url.append("&departYear=" + goDps[0]);
			url.append("&classType=" + cabinType);
			
			//old
			/*url.append("?totalAdults=1&totalChildren=0&totalInfants=0&sortByType=0&isInterlineFlag=false&maxCabinClassMulti=0");
			url.append("&fromCity=" + taskQueue.getFromCity());
			url.append("&toCity=" + taskQueue.getToCity());
			url.append("&departDay=" + goDps[2]);
			url.append("&departMonth=" + goDps[1]);
			url.append("&departYear=" + goDps[0]);
			url.append("&classType=" + cabinType);*/

			// 单程
			if (taskQueue.getIsReturn() == 0) {
				url.append("&classTypeRadioOneWay=" + cabinType);
				url.append("&searchType=ON");

			// 往返
			} else {
				reDps = taskQueue.getReturnGrabDate().split("-");
				url.append("&returnDay=".concat(reDps[2]));
				url.append("&returnMonth=".concat(reDps[1]));
				url.append("&returnYear=".concat(reDps[0]));
				url.append("&classTypeRadioReturn=" + cabinType);
//				url.append("&sortByTypeRadioMiles=SF");
//				url.append("&sortByTypeRadioReturn=0");
//				url.append("&flexiDatePage=false");
				url.append("&searchType=RE");
			}
		} finally {
			goDps = null;
			reDps = null;
		}
		return url.toString();
	}

	/** 获取航班的详细数据。 */
	private String fetchDetailData(int index,int gORr) throws Exception {
		HttpRequestBase request = null;
		String page = null;
		try {
			for (int i = 0; i < detail_retry_count; i++) {
				try {
//					request = new HttpPost(String.format(detail_url, index));
					request = new HttpPost(String.format(getDeataUrl(gORr), index));
					page = super.excuteRequest(getHttpClient(), request, true);
					super.setLenghtCount(page.length());
					break; // 获取成功，退出循环
				} catch (Exception e) {
					logger.error(String.format("加载详细页面[%s]失败：%s,现在进行第[%s]次重试", index, e.getMessage(), i + 1));
				}
			}
		} finally {
			request = null;
		}
		return page;
	}

	
	/**获取回程相应信息*/
	public String getRdetaiInfo(String url,String inBrandCode,int inOption,String outBrandCode,int outOption)throws Exception{
		HttpRequestBase request = null;
		String page = null;
		Integer totalSize = 0;
		try {
			// 重试进行抓取
			for (int i = 0; i < cabin_retry_count; i++) {
				try {
					request = new HttpPost(getRDetailURL(url,inBrandCode,inOption+1,outBrandCode,outOption+1));
					page = super.excuteRequest(getHttpClient(), request, true);
					super.setLenghtCount(page.length());
					totalSize = Integer.parseInt(RegHtmlUtil.regStr(page, regexMap.get("reg_totalcount"), 1));
					logger.info(String.format("舱位[%s]加载成功，数量[%s]", "", totalSize));
					break;
				} catch (Exception e) {
					logger.error(String.format("舱位[%s]信息加载失败，进行第[%s]次重试", "", i + 1));
					rollbackProxyIp(false);
					switchProxyipByHttClient();
				}
			}
		} catch (Exception e) {
			throw e;
		} finally {
			request = null;
		}
		rollbackProxyIp(true);
		return page; // 重试之后还是不能成功
		
	}
	
	
	
	/**
	 * ******** ONEWAY *************************************************************************************
	 * */
	/** 国际单程。 */
	private Object fetchInterOneWay() throws Exception {
		List<AbstractPlaneInfoEntity> results = Lists.newArrayList();
		Map<String, AbstractPlaneInfoEntity> map = Maps.newConcurrentMap();

		// 分别获取三个仓位的信息 0:经济 1：商务 2:头等。
		for (int cabinIndex : CABIN.keySet()) {

			// 只加载最低价格的舱位E舱
			if (!load_more_cabin_e) {
				if (cabinIndex >= 0) break;
			}
 
			// 获取航班列表
			String goPage=this.fetchPageDatas(cabinIndex);
			Integer totalSize = Integer.parseInt(RegHtmlUtil.regStr(goPage, regexMap.get("reg_totalcount"), 1));
			
			for (int i = 0; i < totalSize; i++) {
				String detailString = fetchDetailData(i + 1,1);
				AbstractPlaneInfoEntity t = null;

				// 获取价格
				List<String> prices = RegHtmlUtil.regStrs(detailString, regexMap.get("reg_detail_price"));
				String price = prices.get(1); // 价格
				String total = prices.get(4); // 总计
				String tax = (PlaneInfoEntityBuilder.getDouble(prices.get(2)) + PlaneInfoEntityBuilder.getDouble(prices.get(3))) + ""; // 税费

				List<String> details = RegHtmlUtil.retrieveLinks(detailString, regexMap.get("reg_detail_list"), 1);
				List<TransitEntity> transits = Lists.newArrayList();
				String stopTime = RegHtmlUtil.regStr(detailString, regexMap.get("reg_detail_staytime"));
				String transitNames = getPlaneTransits(getStayTime(stopTime), details, transits, false);

				// 用Map过滤相同航班
				t = map.get(transitNames);
				if (t == null) {
					t = getDetailInfo(details.get(0), false);
					PlaneInfoEntityBuilder.getSingleEntity(t).getTransits().addAll(transits);
					map.put(transitNames, t);
					logger.info(String.format("parse:%s", transitNames));
				}
				String cabinName = RegHtmlUtil.regStr(details.get(0), regexMap.get("reg_detail_cabin"), 1);
				CabinEntity cabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinName, null, cabinName, tax, price, total, null, null, CabinEntity.class);
				PlaneInfoEntityBuilder.getSingleEntity(t).getCabins().add(cabin);
				t.setEndTime(transits.get(transits.size() - 1).getEndTime());
			}
		}
		results = new ArrayList<AbstractPlaneInfoEntity>(map.values());
		PlaneInfoEntityBuilder.buildLimitPrice(results);
		return results;
	}

	/**
	 * ******** ROUND *************************************************************************************
	 * */

	/** 国际往返。 */
	private Object fetchInterRound() throws Exception {
		List<AbstractPlaneInfoEntity> results = Lists.newArrayList();
		Map<String, AbstractPlaneInfoEntity> map = Maps.newConcurrentMap();
		Map<String, Map<String, AbstractPlaneInfoEntity>> relationMaps = Maps.newConcurrentMap();
		Map<String, AbstractPlaneInfoEntity> returnMap = null;

		// 分别获取三个仓位的信息 0:经济 1：商务 2:头等。
		for (int cabinIndex : CABIN.keySet()) {
			logger.info(String.format("正在抓取舱位:%s", CABIN.get(cabinIndex)));
			
			String taxT="0";//税收由去程的舱位类型决定
			
			// 只加载最低价格的舱位E舱
			if (!load_more_cabin_e) if (cabinIndex > 0) break;

			// 获取去程航班列表
			String goPage=this.fetchPageDatas(cabinIndex);
			Integer totalSize = Integer.parseInt(RegHtmlUtil.regStr(goPage, regexMap.get("reg_totalcount"), 1));
			
			for (int i = 0; i < totalSize; i++) {
				DoublePlaneInfoEntity doublePI = null;
				try {
					String detailString = fetchDetailData(i + 1,1);//去程详细信息
					//解析去程详细信息（解析出对应的航班信息）
					Map<String ,String>priceMap=new HashMap<String, String>();//存储此去程的机票商品					
					Document doc=Jsoup.parse(detailString);
					
					//航班价格
					Elements as=doc.select("a.d-block");
					if(as.size()>0){
						priceMap=tMapPrice(as);
					}
					
					//航班详细信息
					doublePI=detailInfos(doc,true,doublePI,null,null);//去程
					
					String[] keys = priceMap.keySet().toArray(new String[0]);
					
					//获取该去程对应的回程信息
					String goDetailS=getRdetaiInfo(re_detail_url,null, 0, keys[0].replaceAll(" ", "").toUpperCase(), i);
					int returnSize=Integer.parseInt(RegHtmlUtil.regStr(goDetailS, regexMap.get("reg_totalcount"), 1));//回程总的航班数
					Map<String,String>rPrice=new HashMap<String,String>();
					for(int j=0;j<returnSize;j++){//获取回程信息
						String retrunDetailString="";//回程信息
						
						//点击回程列表
						retrunDetailString = fetchDetailData(j + 1,2);//去程详细信息
						Document docR=Jsoup.parse(retrunDetailString);
						
						//航班价格
						Elements rs=docR.select("a.d-block");
						if(rs.size()>0){
							rPrice=tMapPrice(rs);
						}
						//航班详细信息
						doublePI=detailInfos(docR,false,doublePI,rPrice,priceMap);//回程
						
						if(taxT.equals("0")){
							String[] rkeys = priceMap.keySet().toArray(new String[0]);
							//全程详细回程信息的（税收价格）共要进入两层，直到点到详细信息
							String depDetailS=getRdetaiInfo(all_detail_url, rkeys[0].replaceAll(" ", "").toUpperCase(), j, keys[0].replaceAll(" ", "").toUpperCase(), i);
							//解析出税价
							Elements taxE=Jsoup.parse(depDetailS).select("div#revenueSOC");
							Elements priceE=taxE.select("p.text-right");
							long taxPrices=0l;
							String tax1=priceE.get(1).text().replaceAll("CNY", "").replaceAll(" ", "").replaceAll(",", "").split("\\.")[0];
							String tax2=priceE.get(2).text().replaceAll("CNY", "").replaceAll(" ", "").replaceAll(",", "").split("\\.")[0];
							
							taxPrices=Long.parseLong(tax1)+Long.parseLong(tax2);
							taxT=taxPrices+"";
						}
						
					}
					
					
					//存储此段的价格
					Map<String,String> allPrice=new HashMap<String,String>();
					List<Double> priceM=new ArrayList<Double>();
					for(Map.Entry<String, String>pricemap:priceMap.entrySet()){
						String gKey=pricemap.getKey();
						String gValue=pricemap.getValue();
						
						CabinEntity cabE=new CabinEntity();//去程舱位
						cabE.setCabinType(CABIN.get(cabinIndex));
						cabE.setProductName(gKey);
						cabE.setPrice(Double.parseDouble(gValue));
						
						for(Map.Entry<String, String>rpice:rPrice.entrySet()){
							String bKey=rpice.getKey();
							String bValue=rpice.getValue();
							//分段价格
							//去程舱位
							priceM.add(Double.parseDouble(gValue)+Double.parseDouble(bValue));
						}
						
						doublePI.getCabins().add(cabE);

					}
							
					Collections.sort(priceM);
					
					doublePI.setTotalLowestTaxesPrice(Double.parseDouble(taxT));
					doublePI.setTotalHighestTaxesPrice(Double.parseDouble(taxT));
					doublePI.setSumLowestPrice(priceM.get(0));
					doublePI.setSumHighestPrice(priceM.get(priceM.size()-1));
					//处理去程祼价
					doublePI.setTotalLowestPrice(doublePI.getSumLowestPrice()-doublePI.getTotalLowestTaxesPrice());
					doublePI.setTotalHighestPrice(doublePI.getSumHighestPrice()-doublePI.getTotalHighestTaxesPrice());
					results.add(doublePI);
					
					
					
				/*	
					
					// 开始解析数据
					AbstractPlaneInfoEntity t = null;

					String goString = RegHtmlUtil.regStr(detailString, regexMap.get("reg_detail_relist"), 1);
					List<String> godetails = RegHtmlUtil.retrieveLinks(goString, regexMap.get("reg_detail_list"), 1);
					String reString = RegHtmlUtil.regStr(detailString, regexMap.get("reg_detail_relist"), 2);
					List<String> redetails = RegHtmlUtil.retrieveLinks(reString, regexMap.get("reg_detail_list"), 1);

					List<TransitEntity> transits = Lists.newArrayList();
					String gostopTime = RegHtmlUtil.regStr(goString, regexMap.get("reg_detail_staytime"));
					String gotransitNames = getPlaneTransits(getStayTime(gostopTime), godetails, transits, false);

					// 用Map过滤相同航班
					t = map.get(gotransitNames);
					if (t == null) {
						t = getDetailInfo(godetails.get(0), false);
						PlaneInfoEntityBuilder.getDoubleEntity(t).getTransits().addAll(transits);
						map.put(gotransitNames, t);
						logger.info(String.format("parse:%s", gotransitNames));
					}

					// 获取价格 1 价格 4总计
					List<String> prices = RegHtmlUtil.regStrs(detailString, regexMap.get("reg_detail_price"));
					String price = prices.get(1); // 价格
					String total = prices.get(4); // 总计
					String tax = (PlaneInfoEntityBuilder.getDouble(prices.get(2)) + PlaneInfoEntityBuilder.getDouble(prices.get(3))) + ""; // 税费

					String cabinName = RegHtmlUtil.regStr(godetails.get(0), regexMap.get("reg_detail_cabin"), 1);
					CabinEntity cabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinName, null, cabinName, null, null, null, null, null, CabinEntity.class);
					PlaneInfoEntityBuilder.getDoubleEntity(t).getCabins().add(cabin);
					t.setStartTime(transits.get(0).getStartTime());
					t.setEndTime(transits.get(transits.size() - 1).getEndTime());

					// RETURN-----------------------------
					AbstractPlaneInfoEntity r = null;
					List<ReturnTransitEntity> returnTransits = Lists.newArrayList();
					String restopTime = RegHtmlUtil.regStr(reString, regexMap.get("reg_detail_staytime"));
					String retransitNames = getPlaneTransits(getStayTime(restopTime), redetails, returnTransits, true);

					// 保存对应的回程集合。
					returnMap = relationMaps.get(gotransitNames);
					if (returnMap == null) {
						returnMap = Maps.newConcurrentMap();
						relationMaps.put(gotransitNames, returnMap);
					}
					r = returnMap.get(retransitNames);
					if (r == null) {
						r = getDetailInfo(redetails.get(0), true);
						PlaneInfoEntityBuilder.getReturnDoubleEntity(r).getReturnTransits().addAll(returnTransits);
						returnMap.put(retransitNames, PlaneInfoEntityBuilder.getReturnDoubleEntity(r));
						logger.info(String.format("return:%s", retransitNames));
					}
					String reCabinName = RegHtmlUtil.regStr(redetails.get(0), regexMap.get("reg_detail_cabin"), 1);
					ReturnCabinEntity reCabin = PlaneInfoEntityBuilder.buildCabinInfo(reCabinName, null, reCabinName, null, null, null, null, null, ReturnCabinEntity.class);
					PlaneInfoEntityBuilder.getReturnDoubleEntity(r).getReturnCabins().add(reCabin);
					r.setStartTime(returnTransits.get(0).getStartTime());
					r.setEndTime(returnTransits.get(returnTransits.size() - 1).getEndTime());

					setRelation(cabin, reCabin, tax, price, total, PlaneInfoEntityBuilder.getDoubleEntity(t));*/

				} catch (Exception e) {
					logger.error(String.format("舱位[%s - %s]加载失败。", CABIN.get(cabinIndex), i));
				} finally {

				}
			}
		}
		/*for (String key : map.keySet()) {
			Map<String, AbstractPlaneInfoEntity> rd = relationMaps.get(key);
			logger.info(String.format("%s returns:%s", key, rd.size()));
			for (AbstractPlaneInfoEntity rt : rd.values()) {
				PlaneInfoEntityBuilder.getDoubleEntity(map.get(key)).getReturnPlaneInfos().add((ReturnDoublePlaneInfoEntity) rt);
			}
		}
		logger.info(String.format("抓取完成:%s", map.size()));
		results = new ArrayList<AbstractPlaneInfoEntity>(map.values());
		PlaneInfoEntityBuilder.buildLimitPrice(results); // 设置最高低价。
		*/
		
		return results;
	}
	
	
	public List<Double> getPriceList(Map<String ,String> priceMap){
		List<Double>  list=new ArrayList<Double>();
		if(priceMap.size()>0){
			for(Map.Entry<String, String> map:priceMap.entrySet()){
				String key=map.getKey();
				String value=map.getValue();
				list.add(Double.parseDouble(value));
			}
			Collections.sort(list);
			return list;
		}
		return null;
	}

	public Map<String,String >tMapPrice(Elements rs){
		Map<String ,String> map=new HashMap<String ,String>();	
		if(rs.size()>0){
			for(Element r:rs){
				String goodsName=r.select("div.select-fare-inner-left").first().select("h1").text();
				String goodPrice=r.select("div.color-dark-gray").first().text().replaceAll("CNY", "").replaceAll(" ", "").replaceAll(",", "").split("\\.")[0];
				map.put(goodsName, goodPrice);
			}
		}
		return map;
	}
	
	public DoublePlaneInfoEntity detailInfos(Document doc,boolean isGo,DoublePlaneInfoEntity doublePI,Map<String ,String> bPriceMap,Map<String ,String> gPriceMap ) throws Exception{
		//航班信息
		Elements els =doc.getElementsByClass("body");
		if(els.size()>0){
			ReturnDoublePlaneInfoEntity returnPI = null;
			for(int k=0;k<els.size();k++){
				org.jsoup.nodes.Element el=els.get(k);
				String cityStr[]=el.getElementsByClass("table-header").first().text().split("至");
				String fromCity =cityStr[0];
				String toCity =cityStr[1];
				//出发城市信息
				String fromCityName=RegHtmlUtil.regStr(fromCity, "(.*)\\(",1);
				String fromCityCode=RegHtmlUtil.regStr(fromCity, "\\((.*)\\)",1);
				//到达城市信息
				String toCityName=RegHtmlUtil.regStr(toCity, "(.*)\\(",1);
				String toCityCode=RegHtmlUtil.regStr(toCity, "\\((.*)\\)",1);
				//航班号，飞机类型，舱位类型
				Elements typeInfo =el.getElementsByClass("table-body-cell-left").first().select("p");
				String NO=typeInfo.get(0).text();//getElementsByClass("fs-22").first().text();
				String type= typeInfo.get(1).text();
				String cabinType= typeInfo.get(2).text();
				
				Elements timeEls=el.getElementsByClass("table-body-cell-inner");
				//飞行时间信息
				org.jsoup.nodes.Element timeEl=timeEls.get(1);
				Elements timeDivs=timeEl.select("div.table-inner-content");
				String startTimeStr=timeDivs.get(0).select("p").first().text();//出发时间
				String endTimeStr=timeDivs.get(1).select("p").first().text();;//到达时间
				Long flightTime=TflighTime(timeDivs.get(2).select("p").text());//飞行时间
				
				//日期信息
				org.jsoup.nodes.Element dateEl=timeEls.get(2);
				Elements dateDivs =dateEl.select("div.table-inner-content");
				//起飞日期
				String beDateStr=dateDivs.get(0).select("p").first().text();
				//到达日期
				String endDateStr=dateDivs.get(1).select("p").first().text();
				
				//完整飞行时间信息
				String beginFliStr=Tdate(taskQueue.getFlightDate().substring(0,4),startTimeStr,beDateStr)+":00";
				String endFliStr=Tdate(taskQueue.getFlightDate().substring(0,4),endTimeStr,endDateStr)+":00";
				
				
				if (isGo) {// 返往，去程
					if(k==0){
						doublePI = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, AIRPORTCODE, AIRPORTNAME,AIRPORTNAME, beginFliStr, null, NO, null, null, null, null, null, null, null, null, DoublePlaneInfoEntity.class);
						doublePI.setFlightDuration(flightTime);
					}
					if(els.size()>1){
						if(k==els.size()-1){
							doublePI.setEndTime(DateUtil.string2Date(endFliStr, "yyyy-MM-dd HH:mm:ss"));
						}
					}else{
						doublePI.setEndTime(DateUtil.string2Date(endFliStr, "yyyy-MM-dd HH:mm:ss"));
					}
				} else {// 返往，回程
					if(k==0){
						returnPI = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, AIRPORTCODE, AIRPORTNAME, AIRPORTNAME, beginFliStr, null, NO, null, null, null, null, null, null, null, null, ReturnDoublePlaneInfoEntity.class);
						returnPI.setFlightDuration(flightTime);
					}
					if(els.size()>1){
						if(k==els.size()-1){
							returnPI.setEndTime(DateUtil.string2Date(endFliStr, "yyyy-MM-dd HH:mm:ss"));
						}
					}else{
						returnPI.setEndTime(DateUtil.string2Date(endFliStr, "yyyy-MM-dd HH:mm:ss"));
					}
					
				}
				
				//中转信息
				if(els.size()>1){//含有中转
					if(isGo){
						TransitEntity trEntity=null;//去程中转
						trEntity=PlaneInfoEntityBuilder.buildTransitEntity(
								NO, NO, AIRPORTCODE, AIRPORTNAME, AIRPORTNAME, fromCityCode, fromCityName,toCityCode, toCityName,type,TransitEntity.class);
						
						doublePI.getTransits().add(trEntity);
					}else{
						ReturnTransitEntity retran=null;//回程中转
						retran=PlaneInfoEntityBuilder.buildTransitEntity(
								NO, NO,AIRPORTCODE, AIRPORTNAME, AIRPORTNAME, fromCityCode, fromCityName,toCityCode, toCityName,type,ReturnTransitEntity.class);
					
						returnPI.getReturnTransits().add(retran);
					}
				}
				
			}
			if(returnPI !=null && !isGo){
				if(bPriceMap !=null){
					List<Double> listB=new ArrayList<Double>();
					listB=getPriceList(bPriceMap);
					returnPI.setTotalLowestPrice(listB.get(0));
					returnPI.setTotalHighestPrice(listB.get(listB.size()-1));
					
					if(gPriceMap !=null){
						List<Double> listG=new ArrayList<Double>();
						listG=getPriceList(bPriceMap);
						returnPI.setSumLowestPrice(listG.get(0)+listB.get(0));
						returnPI.setSumHighestPrice(listG.get(listG.size()-1)+listB.get(listB.size()-1));
					}
				}
				
				doublePI.getReturnPlaneInfos().add(returnPI);
			}
			return doublePI;
		}
		return null;
	}
	
	
	/** 设置航班详细信息 。 */
	private AbstractPlaneInfoEntity getDetailInfo(String detailStr, Boolean isReturn) throws Exception {
		String flightNo, type;
		AbstractPlaneInfoEntity t = null;
		flightNo = RegHtmlUtil.regStr(detailStr, regexMap.get("reg_detail_flightNo"), 1);
		type = RegHtmlUtil.regStr(detailStr, regexMap.get("reg_detail_type"), 1);
		if (!isReturn) {
			t = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, AIRPORTCODE, AIRPORTNAME, AIRPORTNAME, null, null, flightNo, null, "阿联酋", null, type);
		} else {
			t = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, AIRPORTCODE, AIRPORTNAME, AIRPORTNAME, null, null, flightNo, null, "阿联酋", null, type, ReturnDoublePlaneInfoEntity.class);
		}
		return t;
	}

	/** 设置仓位关系 。 */
	private void setRelation(CabinEntity cabin, ReturnCabinEntity reCabin, String tax, String price, String totalprice, DoublePlaneInfoEntity t) {
		CabinRelationEntity relation = new CabinRelationEntity();
		relation.setCabinId(cabin.getId());
		relation.setReturnCabinId(reCabin.getId());
		relation.setTaxesPrice(PlaneInfoEntityBuilder.getDouble(tax));
		relation.setFullPrice(PlaneInfoEntityBuilder.getDouble(price));
		relation.setTotalFullPrice(PlaneInfoEntityBuilder.getDouble(totalprice));
		t.getCabinRelations().add(relation);
	}
	
	
	public String Tdate(String str1,String str2,String str3){
		String dateStr="";
		List<String> monthStr=getPatternList(str3, "(\\d{1,2})","","");
		dateStr=str1+"-"+(Integer.parseInt(monthStr.get(0))>10?monthStr.get(0):"0"+monthStr.get(0))+"-"+(Integer.parseInt(monthStr.get(1))>10?monthStr.get(1):"0"+monthStr.get(1))+" "+str2;
		return dateStr;
	}
	
	public Long TflighTime(String str){
		Long l=0l;
		List<String> t=new ArrayList<String>();
		t=getPatternList(str, "(\\d{1,2})","","");
		if(t.size()>0){
			l+=Integer.parseInt(t.get(0))*60*60;
			if(t.size()>1){
				l+=Integer.parseInt(t.get(1))*60;
			}
		}
		
		return l*1000;
	}

	
	private List<String> getPatternList(String content, String regEx, String replace, String replacement){
		Pattern p=Pattern.compile(regEx, Pattern.DOTALL|Pattern.MULTILINE);
		Matcher m=p.matcher(content);
		List<String> strList = new ArrayList<String>();
		while(m.find()){
			strList.add(m.group(1).trim().replaceAll(replace, replacement));
		}
		return strList;
	}
	
	/** 设置中转信息。 */
	private <T> String getPlaneTransits(Long stayTime, List<String> flightDeports, List<T> transits, Boolean isReturn) throws Exception {

		String flightNo, type, flightNames = "";
		// 保存中转信息。
		for (int i = 0; i < flightDeports.size(); i++) {

			flightNo = RegHtmlUtil.regStr(flightDeports.get(i), "cell\"><p>(EK\\d{3,4})</p></div>", 1);
			// 1 fromName 2from 3toName 4toName
			List<String> arrdeps = RegHtmlUtil.regStrs(flightDeports.get(i), regexMap.get("reg_detail_deparr"));
			// 1ftime 2fdate 3ttime 4tdate
			List<String> times = RegHtmlUtil.regStrs(flightDeports.get(i), regexMap.get("reg_detail_time"));

			type = RegHtmlUtil.regStr(flightDeports.get(i), regexMap.get("reg_detail_type"), 1);

			flightNames += flightNo;

			if (!isReturn) {
				TransitEntity tr = PlaneInfoEntityBuilder.buildTransitEntity(flightNo, flightNo, AIRPORTCODE, AIRPORTNAME, AIRPORTNAME, arrdeps.get(2), arrdeps.get(1), arrdeps.get(4), arrdeps.get(3), type, TransitEntity.class);
				tr.setStartTime(getDate(times.get(2) + "/" + times.get(1)));
				tr.setEndTime(getDate(times.get(4) + "/" + times.get(3)));
				tr.setStayTime(stayTime);
				transits.add((T) tr);
			} else {
				ReturnTransitEntity tr = PlaneInfoEntityBuilder.buildTransitEntity(flightNo, flightNo, AIRPORTCODE, AIRPORTNAME, AIRPORTNAME, arrdeps.get(2), arrdeps.get(1), arrdeps.get(4), arrdeps.get(3), type, ReturnTransitEntity.class);
				tr.setStartTime(getDate(times.get(2) + "/" + times.get(1)));
				tr.setEndTime(getDate(times.get(4) + "/" + times.get(3)));
				tr.setStayTime(stayTime);
				transits.add((T) tr);
			}
		}
		return flightNames;
	}

	/** 验证数据 。 */
	public void validateData(Object obj) throws Exception {

		if (obj == null) {
			throw new Exception("阿联酋获取数据为空");
		}
		String page = obj.toString();
		page = page.replaceAll("\\s", "");
		if (page.matches(".*Therearenoflightsavailableforthissearch.*")) {
			throw new FlightInfoNotFoundException("没有符合搜索条件的航班");
		}
		if (page.matches(".*eitherflightsareunavailable.*") || page.matches(".*seatsarenotavailableontheflightsfortheroutes/datesyouhaveselected.*")) {
			throw new FlightInfoNotFoundException();
		}
		if (page.matches(".*Unabletoprocesstherequestduetothefollowingreasons.*")) {
			throw new Exception("无法处理该请求");
		}
	}

	/**
	 * ******** UTILS *************************************************************************************
	 * */

	/** 将 这种样式的Fri 20 Dec 13 / 23:05 转化为时间 */
	private Date getDate(String time) throws ParseException {
		Date d = null;
		String pattern = "EEEddMMMyy/HH:mm";
		String temp;
		temp = time.replaceAll("\\s", "");
		d = new SimpleDateFormat(pattern, Locale.ENGLISH).parse(temp);
		return d;
	}

	/**
	 * ******** PARAMS *************************************************************************************
	 * */

	private final static String AIRPORTNAME = "阿联酋航空";
	private final static String AIRPORTCODE = "EK";

	/** 正则 */
	private final static Map<String, String> regexMap = new HashMap<String, String>() {
		private static final long serialVersionUID = -1030831565062976554L;
		{
			put("reg_loadmore", "id=\"showLoadMoreBtn\"\\s+?value=\"true\"/>");
			put("reg_totalcount", "id=\"totalPageCount\"\\s*?value=\"(\\d*?)\"/>");
			put("reg_list", "<div\\s+?id=\"priceDetailsDiv_.+?class=\"default-accordion\\s*?accordion-container.+?>(.*?)</a>");
			put("reg_detail_flightNo", "cell\"><p>(EK\\d{3,4})</p></div>");
			put("reg_detail_cabin", "stop\\(s\\).*?<p>\\s*?(\\w*?)\\s*?</p>");

			StringBuffer pricereg = new StringBuffer();
			pricereg.append("Fare</p>\\s*?<p\\s*?class=\"text\\s*?text-right\\s*?fs-22\\s*?fl-right\">(.*?)</p>.*?");
			pricereg.append("Taxes</p>\\s*?<p\\s*?class=\"text\\s*?text-right\\s*?fs-22\\s*?fl-right\">(.*?)</p>.*?");
			pricereg.append("charges</p>\\s*?<p\\s*?class=\"text\\s*?text-right\\s*?fs-22\\s*?fl-right\">(.*?)</p>.*?");
			pricereg.append("Cost</p>\\s*?<p\\s*?class=\"text\\s*?text-right\\s*?fs-22\\s*?fl-right\">(.*?)</p>");
			put("reg_detail_price", pricereg.toString());// 票价
			// put("reg_detail_price", "Total\\s+?price.+?<strong>CNY\\s+?(.+?)</strong>");//总价

			put("reg_detail_list", "<div\\s*?id=\"flightDtlsDiv_(.+?)/\\s+?Tab");
			put("reg_detail_relist", "icon-plane-green(.*?)Returning(.*?)Summary");
			put("reg_detail_deparr", "airport\\s*?fs-22\\s*?mb-16\">\\n*\\s+(.*?)\\s*\\(([A-Z]{3})\\).*airport\\s*?fs-22\\s*?mb-16\">\\n*\\s+(.*?)\\s*\\(([A-Z]{3})\\)");
			put("reg_detail_time",
					"<div\\s*?class=\"left\\s*?cell\\s*?terminal\">\\s*?<p><strong>(.+?)</strong></p>\\s*?<p>(.+?)</p>\\s*?</div>.*?<div\\s*?class=\"right\\s*?cell\\s*?terminal\">\\s*?<p><strong>(.+?)</strong></p>\\s*?<p>(.+?)</p>\\s*?</div>");
			put("reg_detail_type", "add-flight-info.+?add-flight-info.+?<p>(.+?)</p>");
			put("reg_detail_staytime", "class=\"connection-box\">\\s*?Connection\\s*(.*?)\\s*?</div>");
		}
	};

	private Long getStayTime(String stayTime) {
		Long staytime = 0L;
		try {
			stayTime = stayTime.trim();
			if (RegHtmlUtil.regMathcher(stayTime, "\\d*?hr\\s*?\\d*?min")) {
				String[] strs = stayTime.split(" ");
				Integer hour = Integer.parseInt(strs[0].replace("hr", ""));
				Integer min = Integer.parseInt(strs[1].replace("min", ""));
				staytime = (hour * 60 * 60 * 1000) + (min * 60 * 1000L);
			}
		} catch (Exception e) {
			staytime = 0L;
			logger.info("中转停留时间解析出错，设置默认值为0.");
		}
		return staytime;
	}

	/** 仓位 */
	private final static Map<Integer, String> CABIN = new HashMap<Integer, String>() {
		private static final long serialVersionUID = -8065352468685942934L;
		{
			put(0, "经济舱");
//			put(1, "商务舱");
//			put(2, "头等舱");
		}
	};
}
